﻿using System;
using System.Collections.Generic;
using System.Data;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Text;
using System.IO;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace paddle_ocrlabel
{
    public partial class Form5 : Form
    {
        List<Command> commandList = new List<Command>();

        Canvas canvas = new Canvas();
        Mark markOnMouseDown = null;

        Shape shapeOnMouseDown = null;
        Point pointInShapeOnMouseDown;
        List<PointF> originalPointList;

        String imageFolderPath = "";
        String cacheFolderPath = "";

        public float fScale = 1.0f;

        public PointF duijiaoPoint;

        Point start; //画框的起始点
        bool blnDraw;//判断是否绘制

        bool blnDrawFourPoints;
        Rectangle newRect3;

        List<CircleEntity> allPointList4 = new List<CircleEntity>();

        Rectangle newRect;

        String currentFullImagePath = null;
        float currentImageRate = 1.0f;
        Image currentImage = null;

        private CancellationTokenSource _Cts; //任务取消令牌;
        private AutoResetEvent _AutoResetEvent = new AutoResetEvent(false);//参数传 false，则 WaitOne 时阻塞等待;

        public Form5()
        {
            InitializeComponent();
        }
        
        private void btnAutoLabel_Click(object sender, EventArgs e)
        {
            if (lbImages.Items.Count == 0)
            {
                MessageBox.Show("请先选择要标注的图片目录！", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
                return;
            }

            List<string> orders = new List<string>() { }; //业务数据;

            foreach (var item in lbImages.Items)
            {
                orders.Add(item.ToString());
            }

            string businessName = "标注";

            canvas.Shapes.Clear();
            tvResults.Nodes.Clear();

            String url = null;
            AsyncCallback callback = null;
            if (rboDetect.Checked)
            {
                url = Singleton.Instance().pre_url + "detect";
                callback = DetectCompleate;
            }
            else
            {
                url = Singleton.Instance().pre_url + "recognition";
                callback = RecognitionCompleate;
            }

            Dictionary<String, String> dicParams = new Dictionary<string, string>();
            ShowDialog(businessName, orders, url, dicParams, callback);
        }

        void ShowDialog(string businessName, List<String> orders, String url, Dictionary<String, String> dicParams, AsyncCallback callback)
        {
            FormProgressDialog progressWindow = new FormProgressDialog();
            progressWindow.Text = "任务处理窗口";

            progressWindow.SetColorfulTitle(businessName, Color.DarkOrange, true);
            progressWindow.SetColorfulTitle("正在执行中......", Color.Black);
            progressWindow.SetInfo(null, "", "");

            List<string> leftList = orders.Select(x => x).ToList(); //剩余（未处理）数据;
            int successCount = 0; //成功数量;

            _Cts = new CancellationTokenSource();

            //注册一个将在取消此 CancellationToken 时调用的委托;
            _Cts.Token.Register(async () =>
            {
                ShowInfo("操作终止");

                await Task.Run(() =>
                {
                    _AutoResetEvent.WaitOne(1000 * 5); //等待有可能还在执行的业务方法;

                    if (successCount < orders.Count)
                    {
                        ShowInfo($"{businessName} 有 {orders.Count - successCount} 项任务被终止，可在消息框中查看具体项。");

                        foreach (var leftName in leftList)
                        {
                            ShowInfo($"【{businessName}】的【{leftName}】执行失败，失败原因：【手动终止】。");
                        }
                    }
                });
            });

            progressWindow.OperateAction += () =>
            {
                Task task = new Task(() =>
                {
                    foreach (var order in orders)
                    {
                        //判断是否被取消;
                        if (_Cts.Token.IsCancellationRequested)
                        {
                            break;
                        }

                        progressWindow.BeginInvoke(new Action(() =>
                        {
                            progressWindow.SetInfo(null, $"共{orders.Count}项，已执行{successCount}项", $"当前正在执行：{order}");
                        }));


                        dicParams["image_path"] = imageFolderPath + order;
                        if (!dicParams.ContainsKey("isregion"))
                        {
                            dicParams["image_data"] = imageToBase64(dicParams["image_path"]);
                        }

                        if (invokeNetwork(order, url, dicParams, callback))
                        {
                            successCount++;
                            leftList.RemoveAll(x => x == order);

                            if (_Cts.Token.IsCancellationRequested)
                            {
                                _AutoResetEvent.Set(); //放行 Register 委托处的等待;
                            }
                        }

                        try
                        {
                            progressWindow.BeginInvoke(new Action(() =>
                            {
                                progressWindow.SetProsess(orders.IndexOf(order) + 1, orders.Count);
                            }));
                        }
                        catch (Exception ex)
                        {

                        }
                    }

                    //正常结束则延迟一段时间来让进度条走完;
                    if (!_Cts.Token.IsCancellationRequested)
                    {
                        Thread.Sleep(200);
                    }
                }, _Cts.Token);

                task.Start();
                task.Wait();
            };

            progressWindow.AbortAction += () =>
            {
                _Cts.Cancel();
            };

            var result = progressWindow.ShowDialog();
            int leftCount = orders.Count - successCount;
            if (result == DialogResult.OK || leftCount <= 0)
            {
                ShowInfo($"{businessName} 整体完成。");
            }
            else if (result == DialogResult.Abort)
            {
                //移到 _Cts.Token.Register 处一起判断，不然数目可能不准;
                //ShowInfo($"{businessName} 有 {leftCount} 项任务被终止，可在消息框中查看具体项。");
            }
        }

        private bool invokeNetwork(string order, String url, Dictionary<String, String> dicParams, AsyncCallback callback)
        {
            string errStr = $"{order} 识别失败，失败原因：";

            NetAsync(url, dicParams, callback);

            try
            {
                //业务方法;
                ShowInfo($"【{order}】的 {order} 任务执行成功。");
                return true;
            }
            catch (Exception ex)
            {
                ShowInfo($"{errStr}{ex.Message}");
            }

            return false;
        }

        public void NetAsync(string url, Dictionary<String, String> dicParams, AsyncCallback callback)
        {
            try
            {
                StringBuilder sb = new StringBuilder();
                foreach (var key in dicParams.Keys)
                {
                    sb.Append(key + "=" + dicParams[key] + "&");
                }

                var str = sb.ToString();
                string postData = str.Substring(0, str.Length - 1);
                byte[] data = Encoding.UTF8.GetBytes(postData);

                HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
                request.Method = "POST";
                request.ContentType = "application/x-www-form-urlencoded; charset=UTF-8";


                request.ContentLength = data.Length;
                Stream newStream = request.GetRequestStream();

                // Send the data.
                newStream.Write(data, 0, data.Length);
                newStream.Close();


                NetworkParams networkParams = new NetworkParams()
                {
                    request = request,
                    fileFullPath = dicParams["image_path"]
                };

                request.BeginGetResponse(callback, networkParams);
            }
            catch
            {
                Console.WriteLine(DateTime.Now.ToString("hh:mm:ss") + " 识别失败");
            }
        }

        class NetworkParams
        {
            public HttpWebRequest request { get; set; }
            public String fileFullPath { get; set; }
        }

        public void DetectCompleate(IAsyncResult asyncResult)
        {
            try
            {
                NetworkParams networkParams = (asyncResult.AsyncState as NetworkParams);
                HttpWebRequest req = networkParams.request;
                String imageFullPath = networkParams.fileFullPath;

                HttpWebResponse res = req.EndGetResponse(asyncResult) as HttpWebResponse;

                //获取内容  
                String content = null;
                using (StreamReader reader = new StreamReader(res.GetResponseStream(), Encoding.UTF8))
                {
                    content = reader.ReadToEnd();
                }                

                String fileName = FileUtils.getFileNameOfPath(imageFullPath)[0];

                FileStream fs = new FileStream(cacheFolderPath + fileName + ".detect", FileMode.Create);
                StreamWriter sw = new StreamWriter(fs, Encoding.UTF8);
                sw.WriteLine(content);
                sw.Close();
                fs.Close();

                var result = JSONUtils.Json2Obj<PaddleOCR>(content);

                List<PaddleOCRForPoly> paddles = null;
                if (rboFromTop.Checked)
                {
                    paddles = result.paddle_result.OrderBy(x => x.points[0].Y).ThenByDescending(x => x.points[0].X).ToList();
                }
                else
                {
                    paddles = result.paddle_result.OrderByDescending(x => x.points[0].X).ThenBy(x => x.points[0].Y).ToList();
                }



                this.BeginInvoke((System.Action)(() =>
                {
                    if (imageFullPath == currentFullImagePath)
                    {
                        tvResults.Nodes.Clear();
                        this.canvas.Shapes.Clear();

                        foreach (var paddle in paddles)
                        {
                            TreeNode node = new TreeNode(paddle.ToString());
                            node.Tag = paddle;
                            tvResults.Nodes.Add(node);

                            canvas.Shapes.Add(new Shape()
                            {
                                points = paddle.points
                            });
                        }

                        picPreview.Invalidate();
                        tvResults.ExpandAll();
                    }
                }));
            }
            catch (Exception ex)
            {
                Console.WriteLine(DateTime.Now.ToString("hh:mm:ss") + ex.Message);
            }
        }

        public void RecognitionCompleate(IAsyncResult asyncResult)
        {
            try
            {
                NetworkParams networkParams = (asyncResult.AsyncState as NetworkParams);
                HttpWebRequest req = networkParams.request;
                String imageFullPath = networkParams.fileFullPath;

                HttpWebResponse res = req.EndGetResponse(asyncResult) as HttpWebResponse;

                //获取内容  
                String content = null;
                using (StreamReader reader = new StreamReader(res.GetResponseStream(), Encoding.UTF8))
                {
                    content = reader.ReadToEnd();
                }

                String fileName = FileUtils.getFileNameOfPath(imageFullPath)[0];

                FileStream fs = new FileStream(cacheFolderPath + fileName + ".rec", FileMode.Create);

                StreamWriter sw = new StreamWriter(fs, Encoding.UTF8);
                sw.WriteLine(content);
                sw.Close();
                fs.Close();

                var result = JSONUtils.Json2Obj<PaddleOCR>(content);

                List<PaddleOCRForPoly> paddles = null;
                if (rboFromTop.Checked)
                {
                    paddles = result.paddle_result.OrderBy(x => x.points[0].Y).ThenByDescending(x => x.points[0].X).ToList();
                }
                else
                {
                    paddles = result.paddle_result.OrderByDescending(x => x.points[0].X).ThenBy(x => x.points[0].Y).ToList();
                }


                this.BeginInvoke((System.Action)(() =>
                {
                    if (imageFullPath == currentFullImagePath)
                    {
                        tvResults.Nodes.Clear();
                        this.canvas.Shapes.Clear();

                        foreach (var paddle in paddles)
                        {
                            TreeNode node = new TreeNode(paddle.ToString());
                            node.Tag = paddle;

                            TreeNode subNode = new TreeNode(paddle.text);
                            node.Nodes.Add(subNode);

                            tvResults.Nodes.Add(node);

                            canvas.Shapes.Add(new Shape()
                            {
                                text = paddle.text,
                                points = paddle.points
                            });
                        }

                        picPreview.Invalidate();
                        tvResults.ExpandAll();
                    }
                }));
            }
            catch (Exception ex)
            {
                Console.WriteLine(DateTime.Now.ToString("hh:mm:ss") + ex.Message);
            }
        }

        public void RegregionCompleate(IAsyncResult asyncResult)
        {
            try
            {
                NetworkParams networkParams = (asyncResult.AsyncState as NetworkParams);
                HttpWebRequest req = networkParams.request;
                String imageFullPath = networkParams.fileFullPath;
                String encode = FileUtils.UrlEncode(imageFullPath);

                HttpWebResponse res = req.EndGetResponse(asyncResult) as HttpWebResponse;

                //获取内容  
                String content = null;
                using (StreamReader reader = new StreamReader(res.GetResponseStream(), Encoding.UTF8))
                {
                    content = reader.ReadToEnd();
                }

                if (content.Contains("error") && content.Contains("识别不出"))
                {
                    MessageBox.Show("识别不出！", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
                    return;
                }


                var result = JSONUtils.Json2Obj<PaddleOCRForPoly>(content);

                this.BeginInvoke((System.Action)(() =>
                {
                    if (tvResults.SelectedNode.Parent != null)
                    {
                        tvResults.SelectedNode = tvResults.SelectedNode.Parent;
                    }


                    if (tvResults.SelectedNode.Nodes.Count == 0)
                    {
                        tvResults.SelectedNode.Nodes.Add(result.text);
                    }
                    else
                    {
                        tvResults.SelectedNode.Nodes[0].Text = result.text;
                    }

                    var selectPoly = tvResults.SelectedNode.Tag as PaddleOCRForPoly;
                    String originalText = selectPoly.text;
                    selectPoly.text = result.text;
                    tvResults.SelectedNode.Tag = selectPoly;
                    tvResults.SelectedNode.Expand();

                    foreach (var shape in canvas.Shapes)
                    {
                        if (selectPoly.points[0].X == shape.points[0].X && selectPoly.points[0].Y == shape.points[0].Y
                                           && selectPoly.points[1].X == shape.points[1].X && selectPoly.points[1].Y == shape.points[1].Y
                                           && selectPoly.points[2].X == shape.points[2].X && selectPoly.points[2].Y == shape.points[2].Y
                                           && selectPoly.points[3].X == shape.points[3].X && selectPoly.points[3].Y == shape.points[3].Y
                                           )
                        {
                            shape.text = selectPoly.text;
                            break;
                        }
                    }


                    commandList.Add(new Command()
                    {
                        type = CommandEnum.ModifyText,
                        oldPoly = new PaddleOCRForPoly()
                        {
                            text = originalText,
                            points = new List<PointF>()
                            {
                                new PointF(selectPoly.points[0].X, selectPoly.points[0].Y),
                                new PointF(selectPoly.points[1].X, selectPoly.points[1].Y),
                                new PointF(selectPoly.points[2].X, selectPoly.points[2].Y),
                                new PointF(selectPoly.points[3].X, selectPoly.points[3].Y),
                            }
                        }
                    });
                }));
            }
            catch (Exception ex)
            {
                Console.WriteLine(DateTime.Now.ToString("hh:mm:ss") + ex.Message);
            }
        }

        private void lbImages_MouseCaptureChanged(object sender, EventArgs e)
        {
            if (lbImages.Text == "")
                return;

            String imagePath = imageFolderPath + lbImages.Text;
            if (currentFullImagePath == imagePath || lbImages.Items.Count == 0)
                return;

            if (commandList.Count > 0)
            {
                if (MessageBox.Show("当前图片标注已修改，要保存吗？", "警告", MessageBoxButtons.YesNo) == DialogResult.Yes)
                {
                    btnSave_Click(null, null);
                }
                else
                {
                    commandList.Clear();
                }
            }

            if (currentImage != null)
            {
                currentImage.Dispose();
            }

            Image bmp = Image.FromFile(imagePath);
            currentImage = bmp;

            currentImageRate = bmp.Height / (bmp.Width + 0.0f);
            float pbRate = groupBox2.Height / (groupBox2.Width + 0.0f);
            if (pbRate > currentImageRate)
            {
                fScale = groupBox2.Width / (bmp.Width + 0.0f);
            }
            else
            {
                fScale = groupBox2.Height / (bmp.Height + 0.0f);
            }

            picPreview.Image = new Bitmap(bmp, Convert.ToInt32(bmp.Width * fScale), Convert.ToInt32(bmp.Height * fScale));

            currentFullImagePath = imagePath;

            String cacheFileName = "";
            if (rboDetect.Checked)
            {
                cacheFileName = cacheFolderPath + lbImages.Text + ".detect";
            }
            else
            {
                cacheFileName = cacheFolderPath + lbImages.Text + ".rec";
            }

            showBiaozhu(cacheFileName);
        }

        void showBiaozhu(String cacheFileName)
        {
            tvResults.Nodes.Clear();

            this.canvas.Shapes.Clear();

            picPreview.Invalidate();

            if (!File.Exists(cacheFileName))
                return;

            //获取内容  
            String content = null;
            using (StreamReader reader = new StreamReader(cacheFileName, Encoding.UTF8))
            {
                content = reader.ReadToEnd();
            }

            var result = JSONUtils.Json2Obj<PaddleOCR>(content);
            List<PaddleOCRForPoly> paddles = null;
            if (rboFromTop.Checked)
            {
                paddles = result.paddle_result.OrderBy(x => x.points[0].Y).ThenByDescending(x => x.points[0].X).ToList();
            }
            else
            {
                paddles = result.paddle_result.OrderByDescending(x => x.points[0].X).ThenBy(x => x.points[0].Y).ToList();
            }

            foreach (var paddle in paddles)
            {
                TreeNode node = new TreeNode(paddle.ToString());
                node.Tag = paddle;
                tvResults.Nodes.Add(node);

                if (rboRec.Checked)
                {
                    node.Nodes.Add(paddle.text);
                }

                canvas.Shapes.Add(new Shape()
                {
                    text = paddle.text,
                    points = paddle.points
                });
            }

            tvResults.ExpandAll();
            picPreview.Invalidate();
        }

        private void Form5_Resize(object sender, EventArgs e)
        {
            if (currentFullImagePath != null)
            {
                if (groupBox2.Width == 0 || groupBox2.Height == 0)
                    return;

                shapeOnMouseDown = null;
                originalPointList = null;

                float pbRate = groupBox2.Height / (groupBox2.Width + 0.0f);
                if (pbRate > currentImageRate)
                {
                    fScale = groupBox2.Width / (currentImage.Width + 0.0f);
                }
                else
                {
                    fScale = groupBox2.Height / (currentImage.Height + 0.0f);
                }

                picPreview.Image = new Bitmap(currentImage, Convert.ToInt32(currentImage.Width * fScale), Convert.ToInt32(currentImage.Height * fScale));
            }
        }

        private void picPreview_MouseDown(object sender, MouseEventArgs e)
        {
            if (currentImage != null)
            {
                float safeWidth = currentImage.Width * fScale;
                float safeHeight = currentImage.Height * fScale;

                if (e.X > safeWidth || e.Y > safeHeight)
                    return;


                if (btnFourPoints.Tag.ToString() == "0")
                {
                    blnDrawFourPoints = true;

                    allPointList4.Add(new CircleEntity()
                    {
                        x = Convert.ToInt32(e.X / fScale),
                        y = Convert.ToInt32(e.Y / fScale),
                        r = 5
                    });

                    if (allPointList4.Count == 4)
                    {
                        var polyEntity = new PaddleOCRForPoly()
                        {
                            isNew = true,
                            points = new List<PointF>()
                        };

                        foreach (var item in allPointList4)
                        {
                            var entity = new PointF()
                            {
                                X = item.x,
                                Y = item.y,
                            };

                            polyEntity.points.Add(entity);
                        }

                        canvas.Shapes.Add(new Shape()
                        {
                            isNew = true,
                            points = polyEntity.points
                        });

                        TreeNode node = new TreeNode(polyEntity.ToString());
                        node.Tag = polyEntity;
                        tvResults.Nodes.Add(node);
                        tvResults.SelectedNode = node;

                        allPointList4.Clear();



                        InputForm inputForm = new InputForm();
                        inputForm.StartPosition = FormStartPosition.CenterParent;
                        inputForm.form5 = this;
                        inputForm.originalText = "待标注";
                        if (inputForm.ShowDialog() == DialogResult.OK)
                        {
                            node.Nodes.Add(new TreeNode(inputForm.outputText));
                            tvResults.SelectedNode.Expand();
                        }


                        commandList.Add(new Command()
                        {
                            type = CommandEnum.Add,
                            newPoly = new PaddleOCRForPoly()
                            {
                                text = inputForm.outputText,
                                points = new List<PointF>()
                                {
                                    new PointF(polyEntity.points[0].X, polyEntity.points[0].Y),
                                    new PointF(polyEntity.points[1].X, polyEntity.points[1].Y),
                                    new PointF(polyEntity.points[2].X, polyEntity.points[2].Y),
                                    new PointF(polyEntity.points[3].X, polyEntity.points[3].Y),
                                }
                            }
                        });
                    }
                }
                else if (btnRectLabel.Tag.ToString() == "0")
                {
                    blnDraw = true;
                }
                else
                {
                    int x = Convert.ToInt32(e.X / fScale);
                    int y = Convert.ToInt32(e.Y / fScale);

                    if (e.Button == System.Windows.Forms.MouseButtons.Left)
                        markOnMouseDown = canvas.GetMark(x, y, fScale);
                    if (markOnMouseDown != null)
                    {
                        int duijiao1Pos = (markOnMouseDown.Pos + 2) % 4;
                        PointF point = markOnMouseDown.Shape.points[duijiao1Pos];
                        duijiaoPoint = new PointF(point.X, point.Y);


                        originalPointList = new List<PointF>()
                        {
                            new PointF(markOnMouseDown.Shape.points[0].X, markOnMouseDown.Shape.points[0].Y),
                            new PointF(markOnMouseDown.Shape.points[1].X, markOnMouseDown.Shape.points[1].Y),
                            new PointF(markOnMouseDown.Shape.points[2].X, markOnMouseDown.Shape.points[2].Y),
                            new PointF(markOnMouseDown.Shape.points[3].X, markOnMouseDown.Shape.points[3].Y)
                        };

                        return;
                    }
                    
                    shapeOnMouseDown = canvas.GetShape(x, y);

                    canvas.ClearSelection();

                    if (shapeOnMouseDown != null)
                    {
                        originalPointList = new List<PointF>()
                        {
                            new PointF(shapeOnMouseDown.points[0].X, shapeOnMouseDown.points[0].Y),
                            new PointF(shapeOnMouseDown.points[1].X, shapeOnMouseDown.points[1].Y),
                            new PointF(shapeOnMouseDown.points[2].X, shapeOnMouseDown.points[2].Y),
                            new PointF(shapeOnMouseDown.points[3].X, shapeOnMouseDown.points[3].Y)
                        };

                        pointInShapeOnMouseDown = new Point(
                            Convert.ToInt32(e.Location.X - shapeOnMouseDown.points[0].X * fScale),
                            Convert.ToInt32(e.Location.Y - shapeOnMouseDown.points[0].Y * fScale));
                        shapeOnMouseDown.Selected = true;

                        foreach (TreeNode node in tvResults.Nodes)
                        {
                            var poly = node.Tag as PaddleOCRForPoly;

                            if (shapeOnMouseDown.points[0].X == poly.points[0].X && shapeOnMouseDown.points[0].Y == poly.points[0].Y
                    && shapeOnMouseDown.points[1].X == poly.points[1].X && shapeOnMouseDown.points[1].Y == poly.points[1].Y
                    && shapeOnMouseDown.points[2].X == poly.points[2].X && shapeOnMouseDown.points[2].Y == poly.points[2].Y
                    && shapeOnMouseDown.points[3].X == poly.points[3].X && shapeOnMouseDown.points[3].Y == poly.points[3].Y
                    )
                            {
                                tvResults.SelectedNode = node;
                                break;
                            }
                        }
                    }

                    picPreview.Invalidate();
                }

                start = e.Location;
            }
        }

        private void picPreview_MouseMove(object sender, MouseEventArgs e)
        {
            if (currentImage != null)
            {
                float safeWidth = currentImage.Width * fScale;
                float safeHeight = currentImage.Height * fScale;

                Point tempEndPoint = e.Location; //记录框的位置和大小
                if (e.X > safeWidth)
                {
                    tempEndPoint.X = Convert.ToInt32(safeWidth);
                }

                if (e.Y > safeHeight)
                {
                    tempEndPoint.Y = Convert.ToInt32(safeHeight);
                }

                if (btnRectLabel.Tag.ToString() == "0" && blnDraw)
                {
                    if (e.Button != MouseButtons.Left)
                        return;

                    newRect = new Rectangle();
                    newRect.Location = new Point(
                        Math.Min(start.X, tempEndPoint.X),
                        Math.Min(start.Y, tempEndPoint.Y));
                    newRect.Size = new Size(
                        Math.Abs(start.X - tempEndPoint.X),
                        Math.Abs(start.Y - tempEndPoint.Y));
                }
                else if (btnFourPoints.Tag.ToString() == "0" && blnDrawFourPoints)
                {
                    newRect3 = new Rectangle();
                    newRect3.Location = new Point(Convert.ToInt32(e.X / fScale), Convert.ToInt32(e.Y / fScale));
                    newRect3.Size = new Size(1, 1);
                }
                else
                {
                    if (e.Button == System.Windows.Forms.MouseButtons.Left)
                    {
                        if (markOnMouseDown != null)
                        {
                            int x = Convert.ToInt32(e.X / fScale);
                            int y = Convert.ToInt32(e.Y / fScale);

                            markOnMouseDown.MoveTo(x, y, duijiaoPoint);

                            picPreview.Invalidate();
                            return;
                        }
                        else if (shapeOnMouseDown != null)
                        {
                            int x = Convert.ToInt32((e.X - pointInShapeOnMouseDown.X) / fScale);
                            int y = Convert.ToInt32((e.Y - pointInShapeOnMouseDown.Y) / fScale);
                            shapeOnMouseDown.MoveTo(new Point(x, y));
                            this.Cursor = Cursors.SizeAll;
                        }
                    }
                }

                picPreview.Invalidate();

                int x1 = Convert.ToInt32(e.X / fScale);
                int y1 = Convert.ToInt32(e.Y / fScale);               

                Mark m = canvas.GetMark(x1, y1, fScale);
                if (m != null)
                    this.Cursor = m.Cursor;
                else
                    this.Cursor = Cursors.Default;
            }
        }

        private void picPreview_MouseUp(object sender, MouseEventArgs e)
        {
            blnDraw = false;

            if (picPreview.Image == null)
                return;

            if (picPreview.Image != null && btnRectLabel.Tag.ToString() == "0")
            {
                if (newRect != null && newRect.Width > 0 && newRect.Height > 0)
                {
                    int left = Convert.ToInt32(newRect.X / fScale);
                    int top = Convert.ToInt32(newRect.Y / fScale);
                    int width = Convert.ToInt32(newRect.Width / fScale);
                    int height = Convert.ToInt32(newRect.Height / fScale);

                    var poly = new PaddleOCRForPoly()
                    {
                        isNew = true,
                        points = new List<PointF>()
                                {
                                    new PointF(){X = left, Y = top},
                                    new PointF(){X = left + width, Y = top},
                                    new PointF(){X = left + width, Y = top + height},
                                    new PointF(){X = left, Y = top + height},
                        }
                    };



                    TreeNode node = new TreeNode(poly.ToString());
                    node.Tag = poly;
                    tvResults.Nodes.Add(node);
                    tvResults.SelectedNode = node;

                    canvas.Shapes.Add(new Shape()
                    {
                        isNew = true,
                        points = poly.points
                    });

                    newRect = new Rectangle();

                    InputForm inputForm = new InputForm();
                    inputForm.StartPosition = FormStartPosition.CenterParent;
                    inputForm.form5 = this;
                    inputForm.originalText = "待标注";
                    if (inputForm.ShowDialog() == DialogResult.OK)
                    {
                        node.Nodes.Add(new TreeNode(inputForm.outputText));
                        tvResults.SelectedNode.Expand();
                    }

                    canvas.Shapes[canvas.Shapes.Count - 1].text = inputForm.outputText;


                    commandList.Add(new Command()
                    {
                        type = CommandEnum.Add,
                        newPoly = new PaddleOCRForPoly()
                        {
                            text = inputForm.outputText,
                            points = new List<PointF>()
                            {
                                new PointF(poly.points[0].X, poly.points[0].Y),
                                new PointF(poly.points[1].X, poly.points[1].Y),
                                new PointF(poly.points[2].X, poly.points[2].Y),
                                new PointF(poly.points[3].X, poly.points[3].Y),
                            }
                        }
                    });
                }
            } else if (shapeOnMouseDown != null)
            {
                if (originalPointList[0].X == shapeOnMouseDown.points[0].X && originalPointList[0].Y == shapeOnMouseDown.points[0].Y
                    && originalPointList[1].X == shapeOnMouseDown.points[1].X && originalPointList[1].Y == shapeOnMouseDown.points[1].Y
                    && originalPointList[2].X == shapeOnMouseDown.points[2].X && originalPointList[2].Y == shapeOnMouseDown.points[2].Y
                    && originalPointList[3].X == shapeOnMouseDown.points[3].X && originalPointList[3].Y == shapeOnMouseDown.points[3].Y
                    )
                    return;

                this.Cursor = Cursors.Default;

                tvResults.SelectedNode.Text = shapeOnMouseDown.ToString();
                var poly = tvResults.SelectedNode.Tag as PaddleOCRForPoly;
                poly.points = new List<PointF>() {
                                new PointF(shapeOnMouseDown.points[0].X, shapeOnMouseDown.points[0].Y),
                                new PointF(shapeOnMouseDown.points[1].X, shapeOnMouseDown.points[1].Y),
                                new PointF(shapeOnMouseDown.points[2].X, shapeOnMouseDown.points[2].Y),
                                new PointF(shapeOnMouseDown.points[3].X, shapeOnMouseDown.points[3].Y),
                            };
                tvResults.SelectedNode.Tag = poly;



                commandList.Add(new Command()
                {
                    type = CommandEnum.Move,
                    oldPoly = new PaddleOCRForPoly()
                    {
                        points = new List<PointF>()
                            {
                                new PointF(originalPointList[0].X, originalPointList[0].Y),
                                new PointF(originalPointList[1].X, originalPointList[1].Y),
                                new PointF(originalPointList[2].X, originalPointList[2].Y),
                                new PointF(originalPointList[3].X, originalPointList[3].Y),
                            }
                    },
                    newPoly = new PaddleOCRForPoly()
                    {
                        points = new List<PointF>()
                            {
                                new PointF(poly.points[0].X, poly.points[0].Y),
                                new PointF(poly.points[1].X, poly.points[1].Y),
                                new PointF(poly.points[2].X, poly.points[2].Y),
                                new PointF(poly.points[3].X, poly.points[3].Y),
                            }
                    }
                });

                shapeOnMouseDown = null;
                originalPointList = null;
            }
        }

        private void picPreview_Paint(object sender, PaintEventArgs e)
        {
            if (currentImage != null)
            {
                int safeWidth = Convert.ToInt32(currentImage.Width * fScale);
                int safeHeight = Convert.ToInt32(currentImage.Height * fScale);

                Pen pen = new Pen(Color.Gray, 1);
                pen.DashStyle = System.Drawing.Drawing2D.DashStyle.Custom;
                pen.DashPattern = new float[] { 5, 5 };
                e.Graphics.DrawRectangle(pen, 0, 0, safeWidth, safeHeight);
            }

            e.Graphics.TextRenderingHint = TextRenderingHint.AntiAlias;
            e.Graphics.SmoothingMode = SmoothingMode.HighQuality;

            foreach (Shape shape in canvas.Shapes)
            {
                Color color = Color.Blue;

                if (shape.Selected)
                {
                    color = Color.Red;

                    foreach (Mark m in shape.Marks)
                    {
                        Rectangle rect = new Rectangle(
                           Convert.ToInt32(shape.points[m.Pos].X * fScale - m.MarkSize / 2),
                           Convert.ToInt32(shape.points[m.Pos].Y * fScale - m.MarkSize / 2),
                           m.MarkSize, m.MarkSize);

                        e.Graphics.FillEllipse(Brushes.Aqua, rect);
                        e.Graphics.DrawEllipse(Pens.Blue, rect);
                    }
                }
                else
                {
                    if (shape.isNew)
                    {
                        color = Color.Orange;
                    }
                }

                PointF p1 = new PointF(shape.points[0].X * fScale, shape.points[0].Y * fScale);
                PointF p2 = new PointF(shape.points[1].X * fScale, shape.points[1].Y * fScale);
                PointF p3 = new PointF(shape.points[2].X * fScale, shape.points[2].Y * fScale);
                PointF p4 = new PointF(shape.points[3].X * fScale, shape.points[3].Y * fScale);

                e.Graphics.DrawPolygon(new Pen(color, 2), new PointF[4] { p1, p2, p3, p4 });
            }

            if (blnDraw)
            {
                if (newRect != null && newRect.Width > 0 && newRect.Height > 0)
                {
                    e.Graphics.DrawRectangle(new Pen(Color.Orange, 2), newRect);
                }
            }

            if (blnDrawFourPoints && newRect3 != null)
            {
                int new_x = Convert.ToInt32(newRect3.X * fScale);
                int new_y = Convert.ToInt32(newRect3.Y * fScale);

                if (allPointList4.Count == 1)
                {
                    Pen pen = new Pen(Color.Orange, 2);
                    pen.DashStyle = DashStyle.Dash;

                    int p0_x = Convert.ToInt32(allPointList4[0].x * fScale);
                    int p0_y = Convert.ToInt32(allPointList4[0].y * fScale);
                    e.Graphics.DrawLine(pen, new_x, new_y, p0_x, p0_y);
                }
                else if (allPointList4.Count == 2)
                {
                    Pen pen = new Pen(Color.Orange, 2);
                    pen.DashStyle = DashStyle.Dash;

                    int p0_x = Convert.ToInt32(allPointList4[0].x * fScale);
                    int p0_y = Convert.ToInt32(allPointList4[0].y * fScale);
                    int p1_x = Convert.ToInt32(allPointList4[1].x * fScale);
                    int p1_y = Convert.ToInt32(allPointList4[1].y * fScale);
                    e.Graphics.DrawLine(pen, p0_x, p0_y, p1_x, p1_y);
                    e.Graphics.DrawLine(pen, new_x, new_y, p1_x, p1_y);
                }
                else if (allPointList4.Count == 3)
                {
                    int p0_x = Convert.ToInt32(allPointList4[0].x * fScale);
                    int p0_y = Convert.ToInt32(allPointList4[0].y * fScale);
                    int p1_x = Convert.ToInt32(allPointList4[1].x * fScale);
                    int p1_y = Convert.ToInt32(allPointList4[1].y * fScale);
                    int p2_x = Convert.ToInt32(allPointList4[2].x * fScale);
                    int p2_y = Convert.ToInt32(allPointList4[2].y * fScale);

                    Pen pen = new Pen(Color.Orange, 2);
                    pen.DashStyle = DashStyle.Dash;
                    e.Graphics.DrawLine(pen, p0_x, p0_y, p1_x, p1_y);
                    e.Graphics.DrawLine(pen, p1_x, p1_y, p2_x, p2_y);
                    e.Graphics.DrawLine(pen, new_x, new_y, p0_x, p0_y);
                    e.Graphics.DrawLine(pen, new_x, new_y, p2_x, p2_y);
                }
            }
        }

        private void btnSingleLabel_Click(object sender, EventArgs e)
        {
            if (lbImages.Items.Count == 0)
            {
                MessageBox.Show("请先选择要标注的图片目录！", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
                return;
            }

            if (lbImages.Text == "")
            {
                MessageBox.Show("请先选择要标注的图片！", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
                return;
            }

            this.canvas.Shapes.Clear();
            tvResults.Nodes.Clear();

            List<string> orders = new List<string>() { lbImages.Text }; //业务数据;

            string businessName = "标注";

            String url = null;
            AsyncCallback callback = null;
            if (rboDetect.Checked)
            {
                url = Singleton.Instance().pre_url + "detect";
                callback = DetectCompleate;
            }
            else
            {
                url = Singleton.Instance().pre_url + "recognition";
                callback = RecognitionCompleate;
            }

            Dictionary<String, String> dicParams = new Dictionary<string, string>();
            ShowDialog(businessName, orders, url, dicParams, callback);
        }

        private void ShowInfo(string messgae)
        {
            //TBMessage.TryBeginInvoke(new Action(() =>
            //{
            //    TBMessage.AppendText($"{messgae}\r\n\r\n");
            //}));
        }

        private void menu51_Click(object sender, EventArgs e)
        {
            SettingForm form = new SettingForm();
            form.ShowDialog();
        }

        private void Form5_Load(object sender, EventArgs e)
        {
            FileUtils.createDirectory(System.Environment.CurrentDirectory + @"\Cache");

            var cacheProxyFilePath = System.Environment.CurrentDirectory + @"\Cache\proxy.txt";
            if (File.Exists(cacheProxyFilePath))
            {
                String content = null;
                using (StreamReader reader = new StreamReader(cacheProxyFilePath, Encoding.UTF8))
                {
                    content = reader.ReadLine();
                }

                if (content.Length > 0)
                {
                    if (!content.EndsWith("/"))
                    {
                        content = content + @"/";
                    }

                    Singleton.Instance().pre_url = content;
                }

            }
        }

        private void rboDetect_CheckedChanged(object sender, EventArgs e)
        {
            if (commandList.Count > 0)
            {
                if (MessageBox.Show("当前图片标注已修改，要保存吗？", "警告", MessageBoxButtons.YesNo) == DialogResult.Yes)
                {
                    btnSave_Click(null, null);
                }
                else
                {
                    commandList.Clear();
                }
            }

            String imagePath = imageFolderPath + lbImages.Text;

            String cacheFileName = "";
            if (rboDetect.Checked)
            {
                cacheFileName = cacheFolderPath + lbImages.Text + ".detect";
            }
            else
            {
                cacheFileName = cacheFolderPath + lbImages.Text + ".rec";
            }

            if (currentImage != null)
            {
                showBiaozhu(cacheFileName);
            }
        }

        private void btnOCRAgain_Click(object sender, EventArgs e)
        {
            if (rboDetect.Checked)
            {
                MessageBox.Show("该功能只有在开启了左上角的识别模式后才能生使用！", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
                return;
            }

            if (tvResults.Nodes.Count == 0)
                return;

            if (tvResults.SelectedNode == null)
            {
                MessageBox.Show("请先选择一个区域！", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
                return;
            }

            PaddleOCRForPoly poly = null;
            //选中的是坐标节点
            if (tvResults.SelectedNode.Parent == null)
            {
                poly = tvResults.SelectedNode.Tag as PaddleOCRForPoly;
            }
            else //选中的是文本
            {
                poly = tvResults.SelectedNode.Parent.Tag as PaddleOCRForPoly;
            }

            List<string> orders = new List<string>() { }; //业务数据;

            foreach (var item in lbImages.Items)
            {
                orders.Add(item.ToString());
            }

            string businessName = "标注某个区域";

            Dictionary<String, String> dicParams = new Dictionary<string, string>();

            int min_x = Convert.ToInt32(poly.points.Min(t => t.X));
            int min_y = Convert.ToInt32(poly.points.Min(t => t.Y));
            int max_x = Convert.ToInt32(poly.points.Max(t => t.X));
            int max_y = Convert.ToInt32(poly.points.Max(t => t.Y));

            int left = min_x;
            int top = min_y;
            int width = max_x - min_x;
            int height = max_y - min_y;

            var newRectReg = new Rectangle();
            newRectReg.Location = new Point(left, top);
            newRectReg.Size = new Size(width, height);

            Bitmap bitmap = new Bitmap(width, height);
            Rectangle resultRectangle = new Rectangle(0, 0, width, height);
            Graphics graphic = Graphics.FromImage(bitmap);
            graphic.DrawImage(currentImage, resultRectangle, newRectReg, GraphicsUnit.Pixel);
            Bitmap saveImage = Image.FromHbitmap(bitmap.GetHbitmap());
            graphic.Dispose();
            bitmap.Dispose();

            dicParams["image_path"] = currentFullImagePath;
            dicParams["image_data"] = imageToBase64(saveImage);
            dicParams["isregion"] = "1";

            dicParams["p1_x"] = (poly.points[0].X - left).ToString();
            dicParams["p1_y"] = (poly.points[0].Y - top).ToString();
            dicParams["p2_x"] = (poly.points[1].X - left).ToString();
            dicParams["p2_y"] = (poly.points[1].Y - top).ToString();
            dicParams["p3_x"] = (poly.points[2].X - left).ToString();
            dicParams["p3_y"] = (poly.points[2].Y - top).ToString();
            dicParams["p4_x"] = (poly.points[3].X - left).ToString();
            dicParams["p4_y"] = (poly.points[3].Y - top).ToString();

            var url = Singleton.Instance().pre_url + "regregion";

            ShowDialog(businessName, new List<String> { lbImages.Text }, url, dicParams, RegregionCompleate);
        }

        String imageToBase64(Image image)
        {
            MemoryStream ms = new MemoryStream();
            image.Save(ms, System.Drawing.Imaging.ImageFormat.Jpeg);
            byte[] arr = new byte[ms.Length];
            ms.Position = 0;
            ms.Read(arr, 0, (int)ms.Length);
            ms.Close();
            return Convert.ToBase64String(arr);
        }

        String imageToBase64(String imagePath)
        {
            System.IO.StreamReader sr = new StreamReader(imagePath, Encoding.Default, true);
            int index;
            //实例化一个内存流
            System.IO.MemoryStream tempStream = new MemoryStream();
            //将流转换为字节数组
            while ((index = sr.BaseStream.ReadByte()) != -1)
            {
                tempStream.WriteByte(((byte)index));
            }
            byte[] array = tempStream.ToArray();
            tempStream.Close();
            //将得到的字节数组转换为base64位编码
            return Convert.ToBase64String(array);
        }

        private void btnRectLabel_Click(object sender, EventArgs e)
        {
            if (rboDetect.Checked)
            {
                MessageBox.Show("该功能只有在开启了左上角的识别模式后才能生使用！", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
                return;
            }

            if (currentImage == null)
                return;

            //0为正在标注，1为关闭标注
            if (btnRectLabel.Tag.ToString() == "1")
            {
                btnRectLabel.Text = "停止矩阵标注";
                btnRectLabel.Tag = "0";

                btnFourPoints.Text = "启动四点标注";
                btnFourPoints.Tag = "1";
            }
            else
            {
                btnRectLabel.Text = "启动矩形标注";
                btnRectLabel.Tag = "1";
            }
        }

        private void tvResults_NodeMouseClick(object sender, TreeNodeMouseClickEventArgs e)
        {
            if (tvResults.SelectedNode != null)
            {
                if (e.Node == tvResults.SelectedNode)
                    return;

                if (e.Node.Parent != null && e.Node.Parent == tvResults.SelectedNode)
                    return;

                if (tvResults.SelectedNode.Parent != null && e.Node == tvResults.SelectedNode.Parent)
                    return;
            }

            PaddleOCRForPoly newPoly;
            if (e.Node.Parent != null)
            {
                newPoly = e.Node.Parent.Tag as PaddleOCRForPoly;
            }
            else
            {
                newPoly = e.Node.Tag as PaddleOCRForPoly;
            }

            foreach (var shape in canvas.Shapes)
            {
                if (shape.Selected)
                {
                    shape.Selected = false;
                    continue;
                }

                if (newPoly.points[0].X == shape.points[0].X && newPoly.points[0].Y == shape.points[0].Y
                    && newPoly.points[1].X == shape.points[1].X && newPoly.points[1].Y == shape.points[1].Y
                    && newPoly.points[2].X == shape.points[2].X && newPoly.points[2].Y == shape.points[2].Y
                    && newPoly.points[3].X == shape.points[3].X && newPoly.points[3].Y == shape.points[3].Y
                    )
                {
                    shape.Selected = true;
                    continue;
                }
            }

            picPreview.Invalidate();
        }

        private void btnSave_Click(object sender, EventArgs e)
        {
            if (currentFullImagePath == null || currentFullImagePath == "")
                return;

            PaddleOCR paddleOCR = new PaddleOCR();
            paddleOCR.paddle_result = new List<PaddleOCRForPoly>();
            foreach (var shape in canvas.Shapes)
            {
                paddleOCR.paddle_result.Add(new PaddleOCRForPoly()
                {
                    text = shape.text,
                    points = new List<PointF>() {
                        new PointF(shape.points[0].X, shape.points[0].Y),
                        new PointF(shape.points[1].X, shape.points[1].Y),
                        new PointF(shape.points[2].X, shape.points[2].Y),
                        new PointF(shape.points[3].X, shape.points[3].Y),
                    }
                });
            }

            var content = JSONUtils.Obj2Json(paddleOCR);

            String suffix = "";
            if (rboDetect.Checked)
            {
                suffix = ".detect";
            }
            else
            {
                suffix = ".rec";
            }

            String fileName = FileUtils.getFileNameOfPath(currentFullImagePath)[0];

            FileStream fs = new FileStream(cacheFolderPath + fileName + suffix, FileMode.Create);
            StreamWriter sw = new StreamWriter(fs, Encoding.UTF8);
            sw.WriteLine(content);
            sw.Close();
            fs.Close();

            picPreview.Invalidate();

            commandList.Clear();
        }

        private void btnRollBack_Click(object sender, EventArgs e)
        {
            if (commandList.Count == 0)
            {
                return;
            }

            Command command = commandList[commandList.Count - 1];
            commandList.Remove(command);

            if (command.type == CommandEnum.Add)
            {
                TreeNode findNode = null;
                foreach (TreeNode node in tvResults.Nodes)
                {
                    var poly = node.Tag as PaddleOCRForPoly;

                    if (command.newPoly.points[0].X == poly.points[0].X && command.newPoly.points[0].Y == poly.points[0].Y
                        && command.newPoly.points[1].X == poly.points[1].X && command.newPoly.points[1].Y == poly.points[1].Y
                        && command.newPoly.points[2].X == poly.points[2].X && command.newPoly.points[2].Y == poly.points[2].Y
                        && command.newPoly.points[3].X == poly.points[3].X && command.newPoly.points[3].Y == poly.points[3].Y
                        )
                    {
                        findNode = node;
                        break;
                    }
                }

                tvResults.Nodes.Remove(findNode);

                Shape findShape = null;
                foreach (var shape in canvas.Shapes)
                {
                    if (command.newPoly.points[0].X == shape.points[0].X && command.newPoly.points[0].Y == shape.points[0].Y
                     && command.newPoly.points[1].X == shape.points[1].X && command.newPoly.points[1].Y == shape.points[1].Y
                     && command.newPoly.points[2].X == shape.points[2].X && command.newPoly.points[2].Y == shape.points[2].Y
                     && command.newPoly.points[3].X == shape.points[3].X && command.newPoly.points[3].Y == shape.points[3].Y)
                    {
                        findShape = shape;
                        break;
                    }
                }

                canvas.Shapes.Remove(findShape);
            }
            else if (command.type == CommandEnum.Delete)
            {
                canvas.Shapes.Add(new Shape()
                {
                    text = command.oldPoly.text,
                    points = new List<PointF>() {
                        new PointF(command.oldPoly.points[0].X, command.oldPoly.points[0].Y),
                        new PointF(command.oldPoly.points[1].X, command.oldPoly.points[1].Y),
                        new PointF(command.oldPoly.points[2].X, command.oldPoly.points[2].Y),
                        new PointF(command.oldPoly.points[3].X, command.oldPoly.points[3].Y),
                    }
                });

                TreeNode node = new TreeNode(command.oldPoly.ToString());
                node.Tag = command.oldPoly;

                TreeNode subNode = new TreeNode(command.oldPoly.text);
                node.Nodes.Add(subNode);

                tvResults.Nodes.Add(node);
            }
            else if (command.type == CommandEnum.ModifyText)
            {
                Shape findShape = null;
                foreach (var shape in canvas.Shapes)
                {
                    if (command.oldPoly.points[0].X == shape.points[0].X && command.oldPoly.points[0].Y == shape.points[0].Y
                     && command.oldPoly.points[1].X == shape.points[1].X && command.oldPoly.points[1].Y == shape.points[1].Y
                     && command.oldPoly.points[2].X == shape.points[2].X && command.oldPoly.points[2].Y == shape.points[2].Y
                     && command.oldPoly.points[3].X == shape.points[3].X && command.oldPoly.points[3].Y == shape.points[3].Y)
                    {
                        findShape = shape;
                        break;
                    }
                }

                findShape.text = command.oldPoly.text;


                TreeNode findNode = null;
                foreach (TreeNode node in tvResults.Nodes)
                {
                    var poly = node.Tag as PaddleOCRForPoly;

                    if (command.oldPoly.points[0].X == poly.points[0].X && command.oldPoly.points[0].Y == poly.points[0].Y
                        && command.oldPoly.points[1].X == poly.points[1].X && command.oldPoly.points[1].Y == poly.points[1].Y
                        && command.oldPoly.points[2].X == poly.points[2].X && command.oldPoly.points[2].Y == poly.points[2].Y
                        && command.oldPoly.points[3].X == poly.points[3].X && command.oldPoly.points[3].Y == poly.points[3].Y
                        )
                    {
                        findNode = node;
                        break;
                    }
                }

                findNode.Tag = command.oldPoly;

                if (findNode.Nodes.Count == 0)
                {
                    findNode.Nodes.Add(new TreeNode(command.oldPoly.text));
                }
                else
                {
                    findNode.Nodes[0].Text = command.oldPoly.text;
                }

                findNode.Expand();
            }
            else if (command.type == CommandEnum.Move)
            {
                Shape findShape = null;
                foreach (var shape in canvas.Shapes)
                {
                    if (command.newPoly.points[0].X == shape.points[0].X && command.newPoly.points[0].Y == shape.points[0].Y
                     && command.newPoly.points[1].X == shape.points[1].X && command.newPoly.points[1].Y == shape.points[1].Y
                     && command.newPoly.points[2].X == shape.points[2].X && command.newPoly.points[2].Y == shape.points[2].Y
                     && command.newPoly.points[3].X == shape.points[3].X && command.newPoly.points[3].Y == shape.points[3].Y)
                    {
                        findShape = shape;
                        break;
                    }
                }

                findShape.points[0] = new PointF(command.oldPoly.points[0].X, command.oldPoly.points[0].Y);
                findShape.points[1] = new PointF(command.oldPoly.points[1].X, command.oldPoly.points[1].Y);
                findShape.points[2] = new PointF(command.oldPoly.points[2].X, command.oldPoly.points[2].Y);
                findShape.points[3] = new PointF(command.oldPoly.points[3].X, command.oldPoly.points[3].Y);


                TreeNode findNode = null;
                foreach (TreeNode node in tvResults.Nodes)
                {
                    var poly = node.Tag as PaddleOCRForPoly;

                    if (command.newPoly.points[0].X == poly.points[0].X && command.newPoly.points[0].Y == poly.points[0].Y
                        && command.newPoly.points[1].X == poly.points[1].X && command.newPoly.points[1].Y == poly.points[1].Y
                        && command.newPoly.points[2].X == poly.points[2].X && command.newPoly.points[2].Y == poly.points[2].Y
                        && command.newPoly.points[3].X == poly.points[3].X && command.newPoly.points[3].Y == poly.points[3].Y
                        )
                    {
                        findNode = node;
                        break;
                    }
                }

                findNode.Text = command.oldPoly.ToString();
                findNode.Tag = command.oldPoly;
            }

            picPreview.Invalidate();
        }

        private void Form5_KeyDown(object sender, KeyEventArgs e)
        {
            if (e.KeyCode == Keys.S && e.Control)
            {
                btnSave_Click(null, null);
            }
            else if (e.KeyCode == Keys.Z && e.Control)
            {
                btnRollBack_Click(null, null);
            }
            else if (e.KeyCode == Keys.Escape)
            {
                if (btnFourPoints.Tag.ToString() == "0" && currentImage != null && allPointList4.Count < 4)
                {
                    allPointList4.Clear();

                    picPreview.Invalidate();
                }
            }
            else if (e.KeyCode == Keys.Delete)
            {
                if (tvResults.SelectedNode != null && tvResults.SelectedNode.Parent == null)
                {
                    tvResults.Nodes.Remove(tvResults.SelectedNode);

                    Shape findItem = null;
                    foreach (var shape in canvas.Shapes)
                    {
                        if (shape.Selected)
                        {
                            findItem = shape;
                            break;
                        }
                    }

                    canvas.Shapes.Remove(findItem);

                    picPreview.Invalidate();


                    commandList.Add(new Command()
                    {
                        type = CommandEnum.Delete,
                        oldPoly = new PaddleOCRForPoly()
                        {
                            text = findItem.text,
                            points = new List<PointF>()
                            {
                                new PointF(findItem.points[0].X, findItem.points[0].Y),
                                new PointF(findItem.points[1].X, findItem.points[1].Y),
                                new PointF(findItem.points[2].X, findItem.points[2].Y),
                                new PointF(findItem.points[3].X, findItem.points[3].Y),
                            }
                        }
                    });
                }
            }
        }

        private void rboFromTop_CheckedChanged(object sender, EventArgs e)
        {
            tvResults.Nodes.Clear();

            List<Shape> newList = canvas.Shapes;

            if (rboFromTop.Checked)
            {

                newList = canvas.Shapes.OrderBy(x => x.points[0].Y).ThenByDescending(x => x.points[0].X).ToList();
            }
            else
            {
                newList = canvas.Shapes.OrderByDescending(x => x.points[0].X).ThenBy(x => x.points[0].Y).ToList();
            }

            foreach (var item in newList)
            {
                TreeNode node = new TreeNode(item.ToString());

                PaddleOCRForPoly poly = new PaddleOCRForPoly()
                {
                    isNew = item.isNew,
                    isSelected = item.Selected,
                    text = item.text,
                    points = new List<PointF>() {
                        new PointF(item.points[0].X, item.points[0].Y),
                        new PointF(item.points[1].X, item.points[1].Y),
                        new PointF(item.points[2].X, item.points[2].Y),
                        new PointF(item.points[3].X, item.points[3].Y),
                    }
                };

                node.Tag = poly;
                tvResults.Nodes.Add(node);

                if (rboRec.Checked)
                {
                    TreeNode subNode = new TreeNode(item.text);
                    node.Nodes.Add(subNode);
                }
            }

            tvResults.ExpandAll();
        }       

        private void menuOpenImageFolder_Click(object sender, EventArgs e)
        {
            FolderBrowserDialog dialog = new FolderBrowserDialog();

            if (dialog.ShowDialog(this) != DialogResult.OK)
            {
                return;
            }

            if (commandList.Count > 0)
            {
                if (MessageBox.Show("当前图片标注已修改，要保存吗？", "警告", MessageBoxButtons.YesNo) == DialogResult.Yes)
                {
                    btnSave_Click(null, null);
                }
                else
                {
                    commandList.Clear();
                }
            }

            lbImages.Items.Clear();
            imageFolderPath = dialog.DirectoryPath + @"\";

            cacheFolderPath = imageFolderPath + @"Cache\";
            FileUtils.createDirectory(cacheFolderPath);

            DirectoryInfo root = new DirectoryInfo(imageFolderPath);
            foreach (FileInfo f in root.GetFiles())
            {
                if (f.Extension.Contains("jpg") || f.Extension.Contains("jpeg") || f.Extension.Contains("png"))
                {
                    lbImages.Items.Add(f.Name);
                }
            }


            toolStripStatusLabel1.Text = "当前图片目录：" + imageFolderPath;

            tvResults.Nodes.Clear();
            this.canvas.Shapes.Clear();

            currentImage = null;
            currentFullImagePath = null;
            currentImageRate = 1.0f;
        }

        public class CircleEntity
        {
            public int r { get; set; }
            public int x { get; set; }
            public int y { get; set; }
        }

        private void btnFourPoints_Click(object sender, EventArgs e)
        {
            if (rboDetect.Checked)
            {
                MessageBox.Show("该功能只有在开启了左上角的识别模式后才能生使用！", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
                return;
            }

            if (currentImage == null)
                return;

            //0为正在标注，1为关闭标注
            if (btnFourPoints.Tag.ToString() == "1")
            {
                btnFourPoints.Text = "停止四点标注";
                btnFourPoints.Tag = "0";

                btnRectLabel.Text = "启动矩形标注";
                btnRectLabel.Tag = "1";
            }
            else
            {
                btnFourPoints.Text = "启动四点标注";
                btnFourPoints.Tag = "1";

                allPointList4.Clear();
            }
        }

        private void tvResults_NodeMouseDoubleClick(object sender, TreeNodeMouseClickEventArgs e)
        {
            String originalText = "";
            TreeNode node = e.Node;
            if (node.Parent != null)
            {
                originalText = node.Text;
            }
            else
            {
                originalText = node.Nodes[0].Text;
            }

            InputForm inputForm = new InputForm();
            inputForm.form5 = this;
            inputForm.StartPosition = FormStartPosition.CenterParent;
            inputForm.originalText = originalText;
            if (inputForm.ShowDialog() == DialogResult.OK)
            {
                if (originalText == inputForm.outputText)
                    return;

                PaddleOCRForPoly poly = null;
                if (node.Parent != null)
                {
                    node.Text = inputForm.outputText;
                    poly = node.Parent.Tag as PaddleOCRForPoly;
                }
                else
                {
                    if (node.Nodes.Count == 0)
                    {
                        node.Nodes.Add(new TreeNode(inputForm.outputText));
                    }
                    else
                    {
                        node.Nodes[0].Text = inputForm.outputText;
                    }

                    poly = node.Tag as PaddleOCRForPoly;
                }

                foreach (var shape in canvas.Shapes)
                {
                    if (poly.points[0].X == shape.points[0].X && poly.points[0].Y == shape.points[0].Y
                     && poly.points[1].X == shape.points[1].X && poly.points[1].Y == shape.points[1].Y
                     && poly.points[2].X == shape.points[2].X && poly.points[2].Y == shape.points[2].Y
                     && poly.points[3].X == shape.points[3].X && poly.points[3].Y == shape.points[3].Y)
                    {
                        shape.text = inputForm.outputText;
                        break;
                    }
                }

                commandList.Add(new Command()
                {
                    type = CommandEnum.ModifyText,
                    oldPoly = new PaddleOCRForPoly()
                    {
                        text = inputForm.originalText,
                        points = new List<PointF>()
                            {
                                new PointF(poly.points[0].X, poly.points[0].Y),
                                new PointF(poly.points[1].X, poly.points[1].Y),
                                new PointF(poly.points[2].X, poly.points[2].Y),
                                new PointF(poly.points[3].X, poly.points[3].Y),
                            }
                    }
                });
            }
        }

        private void btnExportBiaozhu_Click(object sender, EventArgs e)
        {
            if (imageFolderPath == null || imageFolderPath == "")
            {
                MessageBox.Show("请先选择要标注的图片目录！", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
                return;
            }

            String suffix = "";
            if (rboDetect.Checked)
            {
                suffix = ".detect";
            }
            else
            {
                suffix = ".rec";
            }

            DirectoryInfo di = new DirectoryInfo(imageFolderPath + @"Cache\");
            FileInfo[] fis = di.GetFiles();

            FileStream fs = new FileStream(imageFolderPath + suffix.Substring(1) + ".log", FileMode.Create);
            StreamWriter sw = new StreamWriter(fs, Encoding.UTF8);

            foreach (FileInfo fi in fis)
            {
                if (fi.Extension.EndsWith(suffix))
                {
                    String content = null;
                    using (StreamReader reader = new StreamReader(fi.FullName, Encoding.UTF8))
                    {
                        content = reader.ReadToEnd();
                    }

                    PaddleOCR paddleOCR = JSONUtils.Json2Obj<PaddleOCR>(content);
                    if (paddleOCR == null)
                        continue;

                    PaddleOCRForOutput output = new PaddleOCRForOutput()
                    {
                        paddle_result = new List<PaddleOCREntityForOutput>(),
                    };

                    foreach (var entity in paddleOCR.paddle_result)
                    {
                        String text = "###";
                        if (entity.text != null && entity.text != "待标注" && entity.text.Trim() != "")
                        {
                            text = entity.text;
                        }

                        output.paddle_result.Add(new PaddleOCREntityForOutput()
                        {
                            transcription = text,
                            points = new List<Point>()
                            {
                                new Point((int)entity.points[0].X, (int)entity.points[0].Y),
                                new Point((int)entity.points[1].X, (int)entity.points[1].Y),
                                new Point((int)entity.points[2].X, (int)entity.points[2].Y),
                                new Point((int)entity.points[3].X, (int)entity.points[3].Y),
                            }
                        });
                    }

                    sw.WriteLine(fi.Name.Replace(suffix, "") + "\t" + output.ToString());

                }
            }

            sw.Close();
            fs.Close();

            toolStripStatusLabel1.Text = "标注文件导出到：" + imageFolderPath + suffix.Substring(1) + ".log";
        }        
    }
}